// co
// all/race/allsettle/finnally
// queue/promiseQueue
// cacherequest/请求重试
// lazyMan/flow
// promisify
// 模拟微任务
// promiseLike

function co(gen) {
    let g = gen()

    const next = (params) => {
        const { value, done } = g.next(params)
        if (done) return value;
        if (value.then) {
            return value.then((val) => next(val))
        } else {
            return next(value)
        }
    }
    next()
}

// co(function* () {
//     let a = yield Promise.resolve(1);
//     let b = yield Promise.resolve(2);
//     console.log(a + b)
// })



Promise.myAll = function (promises) {
    let res = [];
    let count = 0;
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(result => {
                res[i] = result;
                count++
                if (count === promises.length) {
                    resolve(res)
                }
            }).catch((err) => reject(err))
        }
    })
}
// Promise.myAll([
//     Promise.resolve(1),
//     Promise.resolve(2)
// ]).then((res) => console.log(res))



Promise.myRace = function (promises) {
    return new Promise((resolve, reject) => {
        for (let i = 0; i < promises.length; i++) {
            Promise.resolve(promises[i]).then(result => {
                resolve(result)
            }).catch((err) => reject(err))
        }
    })
}
// Promise.myRace([
//     new Promise((resolve, reject) => setTimeout(resolve, 500, '1')),
//     new Promise((resolve, reject) => setTimeout(resolve, 100, '2'))
// ]).then(res => console.log(res))



Promise.myAllSettled = function (promises) {
    let maps = promises.map(p => {
        return Promise.resolve(p)
            .then(val => ({
                type: 'resolved',
                value: val
            }))
            .catch(err => ({
                type: 'rejected',
                value: err
            }))
    })
    return Promise.all(maps)
}
// Promise.myAllSettled([
//     Promise.resolve(1),
//     0,
//     Promise.reject(new Error('1'))
// ]).then(res => console.log(res))




Promise.prototype.myFinally = function (callback) {
    return this.then(
        (val) => {
            callback(val)
            return val
        },
        (err) => {
            callback(val)
            throw err
        }
    )
}
// Promise.resolve(1).then(res => res).myFinally(res => console.log('----', res))


class Queue {
    constructor() {
        this.list = [];
    }
    add(fn) {
        this.list.push(fn)
        return this
    }
    async task() {
        for (let i = 0; i < this.list.length; i++) {
            await this.list[i]()
        }
    }
}
// const queue = new Queue();
// var delay = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
// queue.add(() => delay(1000).then(() => console.log('task 1')))
//     .add(() => delay(2000).then(() => console.log('task 2')))
//     .task();



class PromiseQueue {
    constructor() {
        this.list = [];
        this.count = 0;
        this.max = 2;
    }
    add(fn) {
        this.list.push(fn)
        this.next();
        return this;
    }
    next() {
        if (this.count >= this.max) return;
        if (!this.list.length) return;
        let fn = this.list.shift();
        let p = fn();
        this.count++;
        p.then(this.complate.bind(this)).catch(this.complate.bind(this))
    }
    complate() {
        this.count--;
        this.next()
    }
}
// const queue = new PromiseQueue();
// var delay = (timeout) => new Promise((resolve) => setTimeout(resolve, timeout))
// queue.add(() => delay(1000).then(() => console.log('task 1')))
//     .add(() => delay(2000).then(() => console.log('task 2')))



let count = 0;
let max = 2;
let list = [];
let map = new Map();
function cacheRequest(url, successCallback, errorCallback) {
    if (map.has(url)) {
        return successCallback(map.get(url))
    }
    list.push({ url, successCallback, errorCallback })
    processQueue()
}
async function processQueue() {
    if (count >= max) return;
    if (!list.length) return;
    let res = list.shift()
    let { url, successCallback, errorCallback } = res;
    count++
    try {
        const result = await fetch(url);
        const data = await result.json();
        map.set(url, data)
        successCallback(data)
    } catch (error) {
        errorCallback(err)
    } finally {
        count--;
        processQueue()
    }
}



function request(url, max) {
    return fetch(url).catch(err => max <= 0 ? Promise.reject(err) : request(url, max - 1))
}



class lazyMan {
    constructor() {
        this.taskList = [];
        setTimeout(() => {
            this.next()
        }, 0)
    }
    log(msg) {
        let task = () => setTimeout(() => {
            console.log(msg)
            this.next()
        }, 0)
        this.taskList.push(task)
        return this;
    }
    sleep(time) {
        let task = () => setTimeout(() => {
            this.next()
        }, time)
        this.taskList.push(task)
        return this;
    }
    next() {
        let task = this.taskList.shift();
        task && task()
    }
}
// new lazyMan().log('1111').sleep(2000).log('22222')


function flow(promises) {
    function next(index) {
        if (!promises[index]) return;
        let res = promises[index]()
        // 判断是否是promise
        if (res && typeof res.then === 'function') {
            res.then(() => next(index + 1))
        } else {
            next(index + 1)
        }
    }
    next(0)
}
const delay = (time) => new Promise(resolve => setTimeout(resolve, time))
flow([
    () => console.log('1'),
    () => console.log('2'),
    () => delay(1000).then(() => console.log('3')),
    () => delay(4000).then(() => console.log('4')),
    () => console.log('5'),
    () => console.log('6'),
])





function promisify(originalFn) {
    return function (...args) {
        return new Promise((resolve, reject) => {
            function callback(err, ...result) {
                if (err) return reject(err)
                return resolve(result.length > 1 ? result : result[0])
            }
            // 在末尾传入callback
            originalFn.call(this, ...args, callback)
        })
    }
}
const { time } = require('console');
// 使用示例
const fs = require('fs');
const { resolve } = require('path');
const readFileAsync = promisify(fs.readFile);
readFileAsync('2023/原理手写4/test/test.txt', { encoding: 'utf8' })
    .then(contents => {
        console.log(contents, 'neiron');
    })
    .catch(error => {
        console.error('An error occurred:', error);
    });




function runMicroTask(func) {
    if (typeof Promise === 'function') {
        Promise.resolve().then(func)
        return
    }

    if (typeof MutationObserver === 'function') {
        var ob = new MutationObserver(func);
        let node = document.createTextNode('')
        ob.observe(node)
        node.data = 1
        return
    }

    if (typeof process.nextTick === 'function') {
        process.nextTick(func)
        return
    }
}


function promiseLike(promise) {
    return promise !== null
        && typeof promise === 'function'
        && typeof promise === 'object'
        && typeof promise.then === "function"
}
